#include "event_driver.h"
#include <iostream>
#include <boost/interprocess/sync/scoped_lock.hpp>

namespace auroral { namespace event {

EventDriver::EventDriver()
	:running(true), returnValue(0)
{
	registerEvent("enter");
	registerEvent("quit");
	pushEvent("enter", shared_ptr<void>());
};

EventDriver::~EventDriver()
{
	while ( !eventQueue.empty())
	{
		processOnce();
	}
};

int EventDriver::registerEvent(std::string eventType)
{
	lock_guard<mutex> lock(mutex_);
	if(handleMap.count(eventType) == 1){
		// a elements with the same key has already existed.
		return 1;
	}
	handleMap[eventType] = HandleList();
	return 0;
};

int EventDriver::unregisterEvent(std::string eventType)
{
	HandleMap::size_type result;
	{
		lock_guard<mutex> lock(mutex_);
		result = handleMap.erase(eventType);
	}
	if (result==1)
	{
		// success
		return 0;
	}
	else
	{
		// not exists
		return 1;
	}
};

int EventDriver::addHandle(std::string eventType, EVENT_HANDLE handle, int32_t priority)
{
	HandleList::iterator itr;
	{
		lock_guard<mutex> lock(mutex_);
		if (handleMap.count(eventType)==0)
		{
			cerr << "error: no such event: " << eventType << endl;
			return -1;
		}
	}
	HANDLE_INFO newHandle;
	newHandle.priority = priority;
	newHandle.handle = handle;
	{
		lock_guard<mutex> lock(mutex_);
		HandleList &handles = handleMap[eventType];
	
		for (itr = handles.begin(); itr != handles.end(); itr++)
		{
			if ((*itr).priority >= newHandle.priority)
			{
				handles.insert(itr, newHandle);
				break;
			}
		}
		if (itr == handles.end()) // bigger than all of them
		{
			handles.insert(itr, newHandle);
		}
	}
	return 0;

};

int EventDriver::removeHandle(std::string eventType, EVENT_HANDLE handle, int32_t priority)
{
	HandleList::iterator itr;
	{
		lock_guard<mutex> lock(mutex_);

		if (handleMap.count(eventType)==0)
		{
			// no such event
			return -1;
		}
		if (handle==NULL)
		{
			// clear all handles
			HandleList &handles = handleMap[eventType];
			handles.clear();
		}
		HANDLE_INFO newHandle;
		newHandle.priority = priority;
		newHandle.handle = handle;
		HandleList &handles = handleMap[eventType];
		handles.remove(newHandle);
		return 0;
	}
};

int EventDriver::emitEvent(std::string eventType, shared_ptr<void> data)
{
	if (!running)
	{
		cerr << "emit a event to a EventDriver not running!" << endl;
		return -1;
	}
	if(eventType == "enter" || eventType == "quit")
	{
		cerr << "you can not emit \"enter\" or \"quit\" event" << endl;
		return -1;
	}
	pushEvent(eventType, data);
	return 0;
};

int EventDriver::exec()
{
	while(running)
	{
		if (!eventQueue.empty())
		{
			for(int i=0; i < 100; i++){
				processOnce();
			}
		}
		else
		{
			boost::this_thread::sleep(boost::posix_time::milliseconds(1));
		}
	}
	while (!eventQueue.empty())
	{
		processOnce();
	}
	pushEvent("quit", shared_ptr<void>());
	processOnce();
	return returnValue;
};

void EventDriver::processOnce()
{
	EVENT_INFO event = popEvent();
	if (event.eventType == "")
	{
		return;
	}
	HandleList handles;
	{
		lock_guard<mutex> lock(mutex_);
		// notice: we make a copy of the handle list.
		if(handleMap.count(event.eventType) == 0)
		{
			// There is not such a event type,
			// or it may have been unregistered.
			return;
		}
		handles = handleMap[event.eventType];
	}
	HandleList::iterator itr;
	
	for (itr = handles.begin(); itr != handles.end(); itr ++)
	{
		// if the handle does not return 0, the event handling is finished.
		if((*itr).handle(*this, event.eventType, event.data) < 0)
		{
			break;
		}
	}
	return;
}

void EventDriver::quit()
{
	running = false;
}

void EventDriver::exit(int value)
{
	returnValue = value;
	running = false;
}

void EventDriver::pushEvent(string eventType, shared_ptr<void> data)
{
	if(handleMap.count(eventType) == 0) {
		cerr << "error: emiting a non-registered event type: " << eventType << endl;
		return;
	}
	EVENT_INFO newEvent;
	newEvent.eventType = eventType;
	newEvent.data = data;
	{
		lock_guard<mutex> lock(mutex_);
		eventQueue.push(newEvent);
	}
}
EVENT_INFO EventDriver::popEvent()
{
	EVENT_INFO event;
	lock_guard<mutex> lock(mutex_);
	if (eventQueue.empty())
	{
		event.eventType = "";
	}
	else
	{
		event = eventQueue.front();
		eventQueue.pop();
	}
	return event;
}


} }; //namespace auroral::event